package com.own.compoent.util.zip.unzip.util;

import com.own.compoent.util.zip.unzip.model.BaseZipFileModel;
import com.own.compoent.util.zip.unzip.model.BaseZipFolderModel;
import com.own.component.common.exception.BusinessException;
import com.own.component.common.util.list.ListUtil;
import lombok.Cleanup;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.tools.zip.ZipFile;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.UUID;

/**
 * UnzipUtil
 *
 * @author chenxueli
 * @date 2022-09-27 16:04:00
 */
@Slf4j
public class UnzipUtil {

    /**
     * 解压文件流信息
     *
     * @param inputStream 文件流信息
     * @return 解压后的文件流信息
     */
    public BaseZipFolderModel unzip(InputStream inputStream) {
        var file = writeTempZipFile(inputStream);
        return unzip(file);
    }

    /**
     * 解压文件信息
     *
     * @param multipartFile 文件信息
     * @return 解压后的文件信息
     */
    public BaseZipFolderModel unzip(MultipartFile multipartFile) {
        try {
            var file = writeTempZipFile(multipartFile.getInputStream());
            return unzip(file);
        } catch (IOException e) {
            throw new BusinessException("文件流信息写入临时文件失败");
        }
    }

    /**
     * 解压文件信息
     *
     * @param file 文件信息
     * @return 解压后的文件信息
     */
    @SuppressWarnings("all")
    public BaseZipFolderModel unzip(File file) {
        // 判断文件是否为zip文件
        var filename = file.getName();
        if (StringUtils.isBlank(filename) || !filename.endsWith("zip")) {
            log.info("传入文件格式不是zip文件:{}", filename);
            throw new BusinessException("传入文件格式错误" + filename);
        }
        var fileContentMap = new LinkedHashMap<String, InputStream>();
        // 对文件进行解析
        try {
            var zipFile = new ZipFile(file);
            var entries = zipFile.getEntries();
            while (entries.hasMoreElements()) {
                var entry = entries.nextElement();
                // 如果是文件夹，直接抛弃
                if (entry.isDirectory() || entry.getName().startsWith("__MACOSX")) {
                    continue;
                }
                fileContentMap.put(entry.getName(), zipFile.getInputStream(entry));
            }
        } catch (Exception e) {
            throw new BusinessException("压缩包解压异常");
        }
        return resolve(fileContentMap);
    }

    /**
     * 将文件流信息写入临时文件
     *
     * @param inputStream 文件流信息
     * @return 临时文件信息
     */
    private File writeTempZipFile(InputStream inputStream) {
        try {
            @Cleanup
            var bis = new BufferedInputStream(inputStream);
            // 创建临时文件信息
            var file = File.createTempFile(UUID.randomUUID().toString(), ".zip");
            @Cleanup
            var bos = new BufferedOutputStream(new FileOutputStream(file));
            int len;
            var buf = new byte[10 * 1024];
            while ((len = bis.read(buf)) != -1) {
                bos.write(buf, 0, len);
            }
            bos.flush();
            return file;
        } catch (IOException e) {
            throw new BusinessException("创建临时zip文件异常");
        }
    }

    /**
     * 整理数据
     *
     * @param fileMap 文件信息
     * @return 整理后的数据
     */
    @SuppressWarnings("all")
    private BaseZipFolderModel resolve(LinkedHashMap<String, InputStream> fileMap) {
        var folderMap = new HashMap<String, BaseZipFolderModel>();
        var parent = new BaseZipFolderModel("");
        folderMap.put("", parent);
        fileMap.forEach((key, value) -> {
            var pairMap = getPath(key);
            var path = pairMap.getLeft();
            var name = pairMap.getRight();
            if (!folderMap.containsKey(path)) {
                // 获取路径的分段信息
                ListUtil.Spilt.to(path, "/")
                        .stream()
                        .reduce("", (total, item) -> {
                            // 获取文件夹
                            var folder = folderMap.get(total);
                            // 设置新的文件夹路径
                            var newPath = total.length() == 0 ? item : (total + "/" + item);
                            var map = folder.getFolderMap();
                            if (!map.containsKey(newPath)) {
                                var temp = new BaseZipFolderModel(newPath);
                                map.put(newPath, temp);
                                folder.getChildren().add(temp);
                                folderMap.put(newPath, temp);
                            }
                            return newPath;
                        });
            }
            folderMap.get(path).getChildren().add(new BaseZipFileModel(name, value));
        });
        return parent;
    }

    /**
     * 获取路径信息
     *
     * @param name 文件名称
     * @return 路径信息
     */
    private Pair<String, String> getPath(String name) {
        var index = name.lastIndexOf("/");
        if (index == -1) {
            return Pair.of("", name);
        }
        return Pair.of(name.substring(0, index), name.substring(index + 1));
    }

}
